package cau.se.project.inspector;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.filechooser.FileFilter;

import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

import cau.se.project.data.CLSXTreeModel;
import cau.se.project.data.method.RowControl;
import cau.se.project.method.CLSXControl;

import com.jeantessier.classreader.ClassfileLoader;
import com.jeantessier.classreader.ClassfileLoaderEventSource;
import com.jeantessier.classreader.LoadListenerVisitorAdapter;
import com.jeantessier.classreader.ModifiedOnlyDispatcher;
import com.jeantessier.classreader.TransientClassfileLoader;
import com.jeantessier.classreader.Visitor;
import com.jeantessier.dependency.ClassNode;
import com.jeantessier.dependency.CodeDependencyCollector;
import com.jeantessier.dependency.GraphCopier;
import com.jeantessier.dependency.Node;
import com.jeantessier.dependency.NodeFactory;
import com.jeantessier.dependency.PackageNode;

public class DependencyInspector {

	Display display;
	Shell shell = new Shell(display);

	// dependency finder 
	NodeFactory factory = new NodeFactory();
	Collection<String> sources = new ArrayList<>();
	Visitor visitor = new CodeDependencyCollector(factory);
	ClassfileLoader loader = new TransientClassfileLoader(new ModifiedOnlyDispatcher(ClassfileLoaderEventSource.DEFAULT_DISPATCHER));

	// chooser
	JFileChooser chooser = new JFileChooser();

	public DependencyInspector(Shell mainShell, Display mainDisplay)
	{
		this.shell = mainShell;
		this.display = mainDisplay;

		resetSettings();

		// loader
		loader.addLoadListener(new LoadListenerVisitorAdapter(visitor));

		// chooser
		chooser.setFileFilter(new FileFilter() {
			@Override
			public String getDescription() {
				return "Class Files(*.class)";
			}

			@Override
			public boolean accept(File f) {
				return f.isDirectory() || f.getName().contains(".class");
			}
		});
		chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
		chooser.setMultiSelectionEnabled(true);
	}

	public boolean addFilesFromChooser(JFrame parentFrame)
	{
		if(chooser.showOpenDialog(parentFrame) == JFileChooser.APPROVE_OPTION) {
			addSourceFiles(chooser.getSelectedFiles());
			String[] str = new String[chooser.getSelectedFiles().length];
			int i=0;
			for(File f : chooser.getSelectedFiles()) {
				str[i++] = f.getAbsolutePath();
			}

			return true;
		}

		return false;
	}

	public CLSXControl makeTree()
	{
		if(sources.size() == 0) {
			System.out.println("no input files!");
			return null;
		}

		load();

		Collection<PackageNode> nodes = factory.getPackages().values();
		GraphCopier copier = new GraphCopier();
		CLSXControl clsx = new CLSXTreeModel();

		copier.traverseNodes(nodes);
		nodes = copier.getScopeFactory().getPackages().values();

		// row 생성
		for(PackageNode p : nodes) {
			for(Node n : p.getClasses()) {
				if ( !n.getName().contains("java") ) {
					clsx.addRow(clsx.makeRow(n.getName()));
				}
			}
		}

		// dependency 붙이기
		for(PackageNode p : nodes) {
			for ( ClassNode classNode : p.getClasses() ) {
				if ( !classNode.getName().contains("java") ) {
					RowControl rc = clsx.getDSM().findRowByName(classNode.getName());

					for ( Node inbound : classNode.getInboundDependencies() ) {
						RowControl rcDep = clsx.getDSM().findRowByName(getOnlyClassName(inbound.getName()));

						if ( rcDep != null ) {
							rcDep.setDependency(rc);
						}
					}
				}
			}
		}

		// 결과 출력
		System.out.println(String.format("DSM created with %d rows", clsx.getDSM().getSize()));
		clsx.getDSM().print();

		return clsx;
	}

	private String getOnlyClassName(String name) {
		if ( name.contains("(") ) {
			return name.replaceAll("\\.\\w+\\(.*\\)", "");
		}

		return name;
	}

	public void resetSettings()
	{
		sources.clear();
	}

	public void addSourceFiles(File[] files)
	{
		List<String> names = new ArrayList<>();
		for(File f : files) {
			names.add(f.getAbsolutePath());
		}
		sources.addAll(names);
	}

	private void load()
	{
		System.out.println("load .class files...");
		loader.load(sources);
		System.out.println("load complete.");
	}
}
